package com.haircut.project.service.finance.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.haircut.project.constant.RecordConstant;
import com.haircut.project.dao.finance.FinanceDeptBatchMapper;
import com.haircut.project.dao.finance.FinanceDeptMapper;
import com.haircut.project.dao.finance.FinanceStaticMapper;
import com.haircut.project.model.finance.FinanceDept;
import com.haircut.project.model.finance.FinanceDeptBatch;
import com.haircut.project.service.finance.IDeptCalcService;
import com.haircut.project.vo.finance.StaticDeptCalcFinaceVo;
import com.haircut.project.vo.finance.StaticDeptFinaceReqVo;
import com.haircut.sys.dao.dept.SyDeptMapper;
import com.haircut.sys.model.dept.SysDept;
import com.modular.constant.NumberConstant;
import com.modular.redis.RedisUtils;
import com.modular.result.PageParams;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Semaphore;

@Service
public class DeptCalcServiceImpl implements IDeptCalcService {
    @Autowired
    private SyDeptMapper syDeptMapper;
    @Autowired
    private FinanceDeptBatchMapper financeDeptBatchMapper;
    @Autowired
    private FinanceDeptMapper financeDeptMapper;
    @Autowired
    private FinanceStaticMapper financeStaticMapper;
    /**
     * 异步处理，每次只处理10条
     */
    private final Semaphore semaphore = new Semaphore(10);
    @Autowired
    @Qualifier("modularSnowflake")
    private Snowflake snowflake;
    @Autowired
    private RedisUtils redisUtils;
    int pageSize = NumberConstant.HUNDRED * NumberConstant.NINE;
    private final Logger logger = LoggerFactory.getLogger(DeptCalcServiceImpl.class);
    /**
     * 计算部门工资
     */
    @Override
    public void calcDeptFinance(String redisKey)
    {

        boolean hasNext = Boolean.TRUE;
        int currPage = NumberConstant.ZERO;
        IPage<?> pageReq = null;
        Page<SysDept> deptPage = null;
        PageParams reqPage = new PageParams();
        reqPage.setPageSize(pageSize);
        Date currDate = DateUtil.date();
        String batchNo = DateUtil.format(currDate, DatePattern.PURE_DATE_PATTERN);
        while(hasNext){
            redisUtils.flushRedis(redisKey);
            currPage ++;
            reqPage.setPageNum(currPage);
            pageReq = Page.of(currPage,pageSize,Boolean.FALSE);
            deptPage = syDeptMapper.findCalcDeptPage(pageReq,reqPage);
            if(ObjectUtil.isNull(deptPage) || CollUtil.isEmpty(deptPage.getRecords())){
                break;
            }
            if(deptPage.getRecords().size() < currPage){
                hasNext = Boolean.FALSE;
            }
            deptPage.getRecords().forEach(p->{
                logger.info("calcDeptFinance deptId {},deptName {} str",p.getId(),p.getDeptName());
                long s = System.currentTimeMillis();
                try{
                    semaphore.acquire();
                    calcDeptBatchFinance(p,redisKey,currDate,batchNo);
                }catch (Exception e){
                    logger.error(StrUtil.format("calcDeptFinance calc err deptId {} deptName {}",p.getId(),p.getDeptName()),e);
                }finally {
                    semaphore.release();
                    logger.info("calcDeptFinance deptId {},deptName {} time {}",p.getId(),p.getDeptName(),(System.currentTimeMillis()-s));
                }
            });
        }
    }

    /**
     * 计算部门数据
     * @param dept
     * @param redisKey
     */
    private void calcDeptBatchFinance(SysDept dept,String redisKey,Date currDate,String batchNo){
        // 如果没有配置分成比例，则不处理
        if(ObjectUtil.isNull(dept.getShareRatio())){
            return;
        }
        int weekDay = DateUtil.dayOfWeek(currDate);
        weekDay = weekDay -1;
        if(ObjectUtil.equal(weekDay,0)){
            weekDay = 7;
        }
        logger.info("calcDeptFinance weekDay {} genDay{}",weekDay,dept.getGenDay());
        // 如果给定的日期和配置的不一致，则不处理
        if(ObjectUtil.notEqual(weekDay,dept.getGenDay())){
            return;
        }
        StaticDeptFinaceReqVo queryVo = calcQueryDate(dept,currDate);
        boolean hasAdd = Boolean.TRUE;
        FinanceDeptBatch deptBatch =financeDeptBatchMapper.findLastByDeptId(dept.getId());
        if(ObjectUtil.isNotNull(deptBatch)){
            // 如果生成类型为已经生成，如果单号和生成类型为
            if (ObjectUtil.equal(batchNo, deptBatch.getBatchNo())) {
                if (ObjectUtil.equal(deptBatch.getGenType(), NumberConstant.TWO)) {
                    return;
                }
                // 没有生成成功，则需要重新生成
                queryVo.setStartDate(deptBatch.getStartTime());
                queryVo.setEndDate(deptBatch.getEndTime());
                hasAdd = Boolean.FALSE;
            }
        }
        if(hasAdd){

            deptBatch = new FinanceDeptBatch();
            deptBatch.setId(snowflake.nextIdStr());
            deptBatch.setStartTime(queryVo.getStartDate());
            deptBatch.setEndTime(queryVo.getEndDate());
            deptBatch.setDeptGenType(dept.getGenType());
            deptBatch.setSalaryType(RecordConstant.SalaryType.NEW_RECORD);
            deptBatch.setDeptGenDay(dept.getGenDay());
            deptBatch.setBatchNo(batchNo);
            deptBatch.setShareRatio(dept.getShareRatio());
            deptBatch.setGenType(NumberConstant.ONE);
            deptBatch.setDeptId(dept.getId());
            deptBatch.setCreateDate(currDate);
            deptBatch.setModifyDate(currDate);
            financeDeptBatchMapper.insert(deptBatch);
        }
        FinanceDept financeDept = initFinanceDept(deptBatch,dept.getId(),batchNo);
        // 计算批次下所有的用户信息
        int deptDataNum = calcDept(queryVo,deptBatch,redisKey,financeDept);
        logger.info("calcDeptFinance hasAdd {} deptDataNum{}",hasAdd,deptDataNum);
        if(deptDataNum  > NumberConstant.ZERO) {
            financeDeptMapper.insert(financeDept);
            LambdaUpdateWrapper<FinanceDeptBatch> modify = Wrappers.lambdaUpdate();
            modify.set(FinanceDeptBatch::getGenType, NumberConstant.TWO);
            modify.set(FinanceDeptBatch::getModifyDate, DateUtil.date());
            modify.eq(FinanceDeptBatch::getId, deptBatch.getId());
            financeDeptBatchMapper.update(modify);
        }else if(hasAdd){
            financeDeptBatchMapper.deleteById(deptBatch.getId());
        }
    }

    /**
     * 计算日期
     * @param dept
     * @return
     */
    private StaticDeptFinaceReqVo calcQueryDate(SysDept dept,Date currDate){
        StaticDeptFinaceReqVo reqVo = new StaticDeptFinaceReqVo();
        Date startDate = null,endDate = null;
        if(ObjectUtil.equal(dept.getGenType(),NumberConstant.ONE)){
            Date tempDate = DateUtil.offsetWeek(currDate,-1);
            endDate = DateUtil.endOfDay(DateUtil.endOfWeek(tempDate));
            startDate =  DateUtil.endOfDay(DateUtil.beginOfWeek(tempDate));
        }else{
            startDate = DateUtil.offsetWeek(currDate,-1);
            endDate = DateUtil.endOfDay(DateUtil.yesterday());
            startDate = DateUtil.beginOfDay(startDate);
        }
        reqVo.setDeptId(dept.getId());
        reqVo.setEndDate(endDate);
        reqVo.setStartDate(startDate);
        return reqVo;
    }

    /**
     * 计算部门数据
     * @param queryVo
     * @param deptBatch
     * @param redisKey
     */
    @Override
    public Integer calcDept(StaticDeptFinaceReqVo queryVo, FinanceDeptBatch deptBatch, String redisKey, FinanceDept financeDept){
        redisUtils.flushRedis(redisKey);
        int deptDataNum = NumberConstant.ZERO;
        List<StaticDeptCalcFinaceVo> finaceVoList = financeStaticMapper.findDeptFinance(queryVo);
        if(CollUtil.isEmpty(finaceVoList)){
            return deptDataNum;
        }
        deptDataNum  = finaceVoList.size();
        calcDeptAmount(finaceVoList,financeDept);
        return deptDataNum;
    }

    /**
     * 计算工资
     * @param recordList
     */
    private void calcDeptAmount(List<StaticDeptCalcFinaceVo> recordList, FinanceDept financeDept){
        for(StaticDeptCalcFinaceVo record:recordList){
            financeDept.setTotalAmount(financeDept.getTotalAmount().add(record.getTotalAmount()));
            financeDept.setCurrCardAmount(financeDept.getCurrCardAmount().add(record.getCurrCardAmount()));
            financeDept.setCurrCashAmount(financeDept.getCurrCashAmount().add(record.getCurrCashAmount()));
            financeDept.setParentCardAmount(financeDept.getParentCardAmount().add(record.getParentCardAmount()));
            financeDept.setParentCashAmount(financeDept.getParentCashAmount().add(record.getParentCashAmount()));
            financeDept.setCompanyCardAmount(financeDept.getCompanyCardAmount().add(record.getCompanyCardAmount()));
            financeDept.setCompanyCashAmount(financeDept.getCompanyCashAmount().add(record.getCompanyCashAmount()));
        }
    }

    /**
     * 初始化工资
     */
    private FinanceDept initFinanceDept(FinanceDeptBatch deptBatch, String deptId,String batchNo){
        FinanceDept dept = new FinanceDept();
        Date currDate = DateUtil.date();
        dept.setId(snowflake.nextIdStr());
        dept.setTotalAmount(BigDecimal.ZERO);
        dept.setBatchId(deptBatch.getId());
        dept.setSubmitFinanceType(NumberConstant.ZERO);
        dept.setSalaryType(RecordConstant.SalaryType.NEW_RECORD);
        dept.setSalaryNo(batchNo);
        dept.setCreateDate(currDate);
        dept.setDeptId(deptId);
        dept.setModifyDate(currDate);
        dept.setCurrCardAmount(BigDecimal.ZERO);
        dept.setCurrCashAmount(BigDecimal.ZERO);
        dept.setParentCardAmount(BigDecimal.ZERO);
        dept.setParentCashAmount(BigDecimal.ZERO);
        dept.setCompanyCardAmount(BigDecimal.ZERO);
        dept.setCompanyCashAmount(BigDecimal.ZERO);
        return dept;
    }
}
